package com.example.apidocserver.domain.document.event.impl;

import com.example.apidocserver.domain.document.dto.ParamDescDbDto;
import com.example.apidocserver.domain.document.entity.ApiParam;
import com.example.apidocserver.domain.document.entity.ApiParamField;
import com.example.apidocserver.domain.document.event.FieldParamDomainEvent;
import com.example.apidocserver.domain.document.service.ApiParamFieldService;
import com.example.apidocserver.domain.document.service.ApiParamService;
import com.example.apidocserver.infrastructure.config.input.ApiYmlConfig;
import com.example.apidocserver.infrastructure.util.entity.ApiFieldUtil;
import com.example.apidocserver.interfaces.vo.ApiInfoAllVo;
import com.example.apidocserver.interfaces.vo.ApiParamDescVo;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;

import static com.example.apidocserver.infrastructure.constants.SystemConstants.*;

/**
 * 字段说明领域事件
 *
 * @author xyqlg
 */
@Service
public class FieldParamDomainEventImpl implements FieldParamDomainEvent {
    @Resource
    private ApiParamService apiParamService;

    @Resource
    private ApiParamFieldService apiParamFieldService;

    @Resource
    private ApiYmlConfig apiYmlConfig;

    @Override
    public void upsertParamDesc(ApiInfoAllVo apiInfoAllVoResult, boolean addFlag) {
        List<ApiParamField> noRltFieldList = new LinkedList<>();

        // 1.已有数据，查询接口参数信息
        ParamDescDbDto paramDescDbDto = getParamDescDbDto(apiInfoAllVoResult.getApiInfo().getId());

        // 2.过滤新增，获取接口增加参数说明对象 和 新增字段说明
        List<ApiParam> apiParamNewList = filterParamFieldNew(apiInfoAllVoResult, noRltFieldList, paramDescDbDto);

        // 3.新增参数对象、字段说明对象
        apiParamService.insertList(apiParamNewList);
        apiParamFieldService.insertNewAuto(noRltFieldList, apiInfoAllVoResult.getApiInfo().getId());

        // 4.将字段说明没有绑定参数的绑定参数
        if (addFlag) {
            apiParamFieldService.createRlt(noRltFieldList, true);
        }
    }

    /**
     * 查询设置参数说明、字段说明
     *
     * @param apiInfoAllVo 接口信息
     */
    @Override
    public void setParamDesc(ApiInfoAllVo apiInfoAllVo) {
        if (Objects.isNull(apiInfoAllVo) || Objects.isNull(apiInfoAllVo.getApiInfo()) ||
                Objects.isNull(apiInfoAllVo.getApiInfo().getId())) {
            return;
        }
        ParamDescDbDto paramDescDbDto = getParamDescDbDto(apiInfoAllVo.getApiInfo().getId());
        apiInfoAllVo.setApiParamDescVo(parseParamDesc(paramDescDbDto));
    }

    @Override
    public ApiParam upsertParamDesc(ApiParam paramDescVo) {
        // 增加或修改参数数据
        apiParamService.upsertParam(paramDescVo);

        // 增加或修改参数字段
        apiParamFieldService.upsertField(paramDescVo);
        return paramDescVo;
    }

    @Override
    public List<ApiParamField> findLikeFields(String world, Integer apiId, Integer spaceId) {
        List<ApiParamField> fieldDbList = apiParamFieldService.findLikeFields("%" + world + "%", spaceId);
        if (CollectionUtils.isEmpty(fieldDbList)) {
            return Collections.emptyList();
        }

        List<ApiParamField> apiList = new ArrayList<>(fieldDbList.size());
        List<ApiParamField> commonList = new ArrayList<>(fieldDbList.size());
        List<ApiParamField> otherList = new ArrayList<>(fieldDbList.size());

        for (ApiParamField field : fieldDbList) {
            field.setDescMode(DM_AFTER);
            if (apiId.equals(field.getApiId())) {
                apiList.add(field);
            } else if (apiYmlConfig.commonApiId.equals(field.getApiId())) {
                commonList.add(field);
            } else {
                otherList.add(field);
            }
        }

        List<ApiParamField> result = new ArrayList<>();
        result.addAll(apiList);
        result.addAll(commonList);
        result.addAll(otherList);
        return result;
    }

    @Override
    public ApiParamField findByUniKey(ApiParamField field) {
        return apiParamFieldService.findByUinKey(field);
    }

    @Override
    public List<ApiParamField> findBySpaceId(Integer spaceId) {
        return apiParamFieldService.findBySpaceId(spaceId);
    }

    @Override
    public boolean deleteById(Integer fieldId) {
        return apiParamFieldService.deleteById(fieldId);
    }

    @Override
    public void upsertOne(ApiParamField field) {
        apiParamFieldService.upsertOne(field);
    }

    @Override
    public void insertBatchForInput(List<ApiParamDescVo> apiParamDescList, Integer userId, Date now) {
        if (CollectionUtils.isEmpty(apiParamDescList)) {
            return;
        }
        // 1.插入参数说明
        List<ApiParam> apiParamList = getApiParams(apiParamDescList, userId, now);
        this.apiParamService.insertList(apiParamList);

        // 2.插入字段说明、绑定参数和字段
        this.apiParamFieldService.insertParamFieldBatch(apiParamList, userId, now);
    }

    private static List<ApiParam> getApiParams(List<ApiParamDescVo> apiParamDescList, Integer userId, Date now) {
        List<ApiParam> apiParamList = new ArrayList<>(apiParamDescList.size() << 2);
        for (ApiParamDescVo apiParamDescVo : apiParamDescList) {
            if (Objects.nonNull(apiParamDescVo.getInputPath())) {
                apiParamList.add(setCreateParam(apiParamDescVo.getInputPath(), userId, now));
            }
            if (Objects.nonNull(apiParamDescVo.getInputHeader())) {
                apiParamList.add(setCreateParam(apiParamDescVo.getInputHeader(), userId, now));
            }
            if (Objects.nonNull(apiParamDescVo.getInputQuery())) {
                apiParamList.add(setCreateParam(apiParamDescVo.getInputQuery(), userId, now));
            }
            if (Objects.nonNull(apiParamDescVo.getOutputHeader())) {
                apiParamList.add(setCreateParam(apiParamDescVo.getOutputHeader(), userId, now));
            }
            if (!CollectionUtils.isEmpty(apiParamDescVo.getInputBody())) {
                for (ApiParam apiParam : apiParamDescVo.getInputBody()) {
                    apiParamList.add(setCreateParam(apiParam, userId, now));
                }
            }
            if (!CollectionUtils.isEmpty(apiParamDescVo.getOutputBody())) {
                for (ApiParam apiParam : apiParamDescVo.getOutputBody()) {
                    apiParamList.add(setCreateParam(apiParam, userId, now));
                }
            }
        }
        return apiParamList;
    }

    private static ApiParam setCreateParam(ApiParam apiParam, Integer userId, Date now) {
        apiParam.setCreateTime(now).setUpdateTime(now).setCreator(userId).setUpdater(userId);
        return apiParam;
    }

    /**
     * 转参数说明vo对象
     *
     * @param paramDescDbDto 数据库已有数据对象
     * @return 接口参数说明对象
     */
    private ApiParamDescVo parseParamDesc(ParamDescDbDto paramDescDbDto) {
        ApiParamDescVo apiParamDescVo = new ApiParamDescVo();
        Map<Integer, List<ApiParam>> typeAndParamsDbMap = paramDescDbDto.getTypeAndParamsDbMap();

        // 设置请求头参数
        if (!CollectionUtils.isEmpty(typeAndParamsDbMap.get(PT_HEADER))) {
            for (ApiParam apiParam : typeAndParamsDbMap.get(PT_HEADER)) {
                if (DT_INPUT.equals(apiParam.getDataType())) {
                    apiParamDescVo.setInputHeader(apiParam);
                    ApiFieldUtil.sort(apiParam.getFields());
                } else {
                    apiParamDescVo.setOutputHeader(apiParam);
                    ApiFieldUtil.sort(apiParam.getFields());
                }
            }
        }

        // 设置路径参数
        if (!CollectionUtils.isEmpty(typeAndParamsDbMap.get(PT_PATH))) {
            apiParamDescVo.setInputPath(typeAndParamsDbMap.get(PT_PATH).get(0));
            ApiFieldUtil.sort(apiParamDescVo.getInputPath().getFields());
        }

        if (!CollectionUtils.isEmpty(typeAndParamsDbMap.get(PT_QUERY))) {
            apiParamDescVo.setInputQuery(typeAndParamsDbMap.get(PT_QUERY).get(0));
            ApiFieldUtil.sort(apiParamDescVo.getInputQuery().getFields());
        }

        setBodyParamDesc(apiParamDescVo, typeAndParamsDbMap);

        return apiParamDescVo;
    }

    /**
     * 设置body参数说明
     *
     * @param apiParamDescVo     参数说明vo对象
     * @param typeAndParamsDbMap 数据库参数说明对象
     */
    private static void setBodyParamDesc(ApiParamDescVo apiParamDescVo, Map<Integer, List<ApiParam>> typeAndParamsDbMap) {
        if (!CollectionUtils.isEmpty(typeAndParamsDbMap.get(PT_BODY))) {
            for (ApiParam apiParam : typeAndParamsDbMap.get(PT_BODY)) {
                ApiFieldUtil.sort(apiParam.getFields());
                if (DT_INPUT.equals(apiParam.getDataType())) {
                    // 设置入参body
                    if (CollectionUtils.isEmpty(apiParamDescVo.getInputBody())) {
                        List<ApiParam> bodyParamList = new ArrayList<>(typeAndParamsDbMap.get(PT_BODY).size());
                        bodyParamList.add(apiParam);
                        apiParamDescVo.setInputBody(bodyParamList);
                    } else {
                        apiParamDescVo.getInputBody().add(apiParam);
                    }
                } else {
                    // 设置返回body
                    if (CollectionUtils.isEmpty(apiParamDescVo.getOutputBody())) {
                        List<ApiParam> bodyParamList = new ArrayList<>(typeAndParamsDbMap.get(PT_BODY).size());
                        bodyParamList.add(apiParam);
                        apiParamDescVo.setOutputBody(bodyParamList);
                    } else {
                        apiParamDescVo.getOutputBody().add(apiParam);
                    }
                }
            }
            if (!CollectionUtils.isEmpty(apiParamDescVo.getInputBody())) {
                apiParamDescVo.getInputBody().sort(Comparator.comparing(ApiParam::getOrderNo));
            }
            if (!CollectionUtils.isEmpty(apiParamDescVo.getOutputBody())) {
                apiParamDescVo.getOutputBody().sort(Comparator.comparing(ApiParam::getOrderNo));
            }
        }
    }

    /**
     * 查询接口参数信息
     *
     * @param apiId 接口id
     * @return 接口参数说明信息
     */
    private ParamDescDbDto getParamDescDbDto(Integer apiId) {
        // 1.查询接口对象参数说明
        List<ApiParam> apiParamDbList = apiParamService.queryByApiId(apiId);

        // 2.根据参数类型分类，并查询对应参数说明对应字段说明对象，记录接口已有字段说明
        return getParamTypeParamsMap(apiParamDbList);
    }


    /**
     * 根据参数类型分类，并查询对应参数说明对应字段说明对象，记录接口已有字段说明
     *
     * @param apiParamDbList 已有参数说明
     * @return 参数说明数据传递对象
     */
    private ParamDescDbDto getParamTypeParamsMap(List<ApiParam> apiParamDbList) {
        ParamDescDbDto paramDescDbDto = new ParamDescDbDto(new Date());

        if (!apiParamDbList.isEmpty()) {
            Map<Integer, ApiParam> idAndParamMap = new HashMap<>(apiParamDbList.size());
            Map<Integer, List<ApiParam>> paramTypeParamsMap = paramDescDbDto.getTypeAndParamsDbMap();

            for (ApiParam apiParam : apiParamDbList) {
                // 1.循环参数说明对象设置 参数说明对象id对应参数说明对象  参数类型对应参数说明对象
                idAndParamMap.put(apiParam.getId(), apiParam);
                Integer paramType = apiParam.getParamType();
                if (!paramTypeParamsMap.containsKey(paramType)) {
                    paramTypeParamsMap.put(paramType, new LinkedList<>());
                }
                paramTypeParamsMap.get(paramType).add(apiParam);
            }

            // 2.查询参数说明对象对应字段说明对象
            Map<Integer, List<ApiParamField>> paramIdAndFieldsMap = apiParamFieldService.queryByParamIds(idAndParamMap.keySet(), false);

            // 3.参数说明对象设置对应字段说明对象
            if (!paramIdAndFieldsMap.isEmpty()) {
                setFieldDesc(paramDescDbDto, idAndParamMap, paramIdAndFieldsMap);
            }
        }
        return paramDescDbDto;
    }

    private static void setFieldDesc(ParamDescDbDto paramDescDbDto, Map<Integer, ApiParam> idAndParamMap, Map<Integer, List<ApiParamField>> paramIdAndFieldsMap) {
        paramIdAndFieldsMap.forEach((paramId, apiParamFields) -> {
            if (idAndParamMap.containsKey(paramId) && !Objects.isNull(idAndParamMap.get(paramId))) {
                ApiParam apiParam = idAndParamMap.get(paramId);
                apiParam.setFields(apiParamFields);
                // 4.设置参数说明字段，一个字段只、一种类型只会有一个对象
                if (!CollectionUtils.isEmpty(apiParamFields)) {
                    apiParam.setFieldKeys(new HashSet<>(apiParamFields.size()));
                    for (ApiParamField field : apiParamFields) {
                        ApiFieldUtil.setDescOut(field);
                        apiParam.getFieldKeys().add(field.getFieldName());
                        apiParam.putField(field);
                        paramDescDbDto.getFieldKeyAndFiledDbMap().put(field.getFieldName(), field);
                    }
                }
            }
        });
    }

    /**
     * 将字符串转换为参数说明对象和字段说明字段
     *
     * @param apiInfoAllVoResult 接口返回信息
     * @param noRltFieldList     没有关联的字符说明字段
     * @param paramDescDbDto     已存在参数说明和当前时间对象
     * @return 参数说明对象
     */
    private List<ApiParam> filterParamFieldNew(ApiInfoAllVo apiInfoAllVoResult, List<ApiParamField> noRltFieldList,
                                               ParamDescDbDto paramDescDbDto) {
        List<ApiParam> apiParamNewList = new LinkedList<>();
        ApiParamDescVo apiParamDescIn = apiInfoAllVoResult.getApiParamDescVo();

        if (filterParamFieldOneNew(apiParamDescIn.getInputHeader(), noRltFieldList, paramDescDbDto)) {
            apiParamNewList.add(apiParamDescIn.getInputHeader());
        }
        if (filterParamFieldOneNew(apiParamDescIn.getOutputHeader(), noRltFieldList, paramDescDbDto)) {
            apiParamNewList.add(apiParamDescIn.getOutputHeader());
        }
        if (filterParamFieldOneNew(apiParamDescIn.getInputPath(), noRltFieldList, paramDescDbDto)) {
            apiParamNewList.add(apiParamDescIn.getInputPath());
        }
        if (filterParamFieldOneNew(apiParamDescIn.getInputQuery(), noRltFieldList, paramDescDbDto)) {
            apiParamNewList.add(apiParamDescIn.getInputQuery());
        }

        handleBodyParam(apiParamDescIn.getInputBody(), noRltFieldList, apiParamNewList, paramDescDbDto);
        handleBodyParam(apiParamDescIn.getOutputBody(), noRltFieldList, apiParamNewList, paramDescDbDto);
        return apiParamNewList;
    }

    private static boolean filterParamFieldOneNew(ApiParam apiParamDescIn, List<ApiParamField> noRltFieldList,
                                                  ParamDescDbDto paramDescDbDto) {
        boolean result = false;
        if (!Objects.isNull(apiParamDescIn)) {
            // 1.获取数据库已有参数对象
            Map<Integer, List<ApiParam>> paramTypeParamsMap = paramDescDbDto.getTypeAndParamsDbMap();
            Integer paramTypeIn = apiParamDescIn.getParamType();
            ApiParam apiParamDb = null;
            if (paramTypeParamsMap.containsKey(paramTypeIn) && !paramTypeParamsMap.get(paramTypeIn).isEmpty()) {
                for (ApiParam apiParam : paramTypeParamsMap.get(paramTypeIn)) {
                    if (apiParamDescIn.getDataType().equals(apiParam.getDataType())) {
                        apiParamDb = apiParam;
                    }
                }
            }
            if (!Objects.isNull(apiParamDb)) {
                // 2.1.接口对应参数说明已存在
                if (!CollectionUtils.isEmpty(apiParamDescIn.getFields())) {
                    compareFields(apiParamDescIn, noRltFieldList, apiParamDb, paramDescDbDto);
                }
            } else {
                // 2.2.接口对应参数说明还维持在
                paramAndNoRltFieldAdd(apiParamDescIn, noRltFieldList, paramDescDbDto);
                result = true;
            }
        }
        return result;
    }

    /**
     * 处理body体参数类型
     *
     * @param bodyParamList   body体参数
     * @param noRltFieldList  未关联字段
     * @param apiParamNewList 新增参数说明对象
     * @param paramDescDbDto  已有参数工具类
     */
    private void handleBodyParam(List<ApiParam> bodyParamList, List<ApiParamField> noRltFieldList,
                                 List<ApiParam> apiParamNewList, ParamDescDbDto paramDescDbDto) {
        Map<Integer, List<ApiParam>> paramTypeParamsMap = paramDescDbDto.getTypeAndParamsDbMap();

        // 已存在参数为空，或body为空
        boolean dbParamIsEmpty = paramTypeParamsMap.isEmpty() || CollectionUtils.isEmpty(paramTypeParamsMap.get(PT_BODY));
        // 入参body说明不为空
        boolean inParamExist = !CollectionUtils.isEmpty(bodyParamList);
        if (dbParamIsEmpty && inParamExist) {
            // 数据库已有参数说明不存在，且输入存在
            for (ApiParam apiParamDescIn : bodyParamList) {
                paramAndNoRltFieldAdd(apiParamDescIn, noRltFieldList, paramDescDbDto);
                apiParamNewList.add(apiParamDescIn);
            }
        } else if (!dbParamIsEmpty && inParamExist) {
            // 数据库已有参数存在
            out:
            for (ApiParam apiParamDescIn : bodyParamList) {
                for (ApiParam apiParamDb : paramTypeParamsMap.get(PT_BODY)) {
                    // 判断是同类型、同参数
                    if (apiParamDb.getDataType().equals(apiParamDescIn.getDataType())
                            && apiParamDb.getParamPath().equals(apiParamDescIn.getParamPath())) {
                        apiParamDescIn.setId(apiParamDb.getId());
                        compareFields(apiParamDescIn, noRltFieldList, apiParamDb, paramDescDbDto);
                        continue out;
                    }
                }
                // 没有同类型、同参数则是新增参数
                paramAndNoRltFieldAdd(apiParamDescIn, noRltFieldList, paramDescDbDto);
                apiParamNewList.add(apiParamDescIn);
            }
        }
    }

    /**
     * 接口的对应参数已有参数说明和字段说明，对字段解析比较
     *
     * @param apiParamDescIn 参数字符串转换的参数说明
     * @param noRltFieldList 没有绑定关系的字段说明
     * @param apiParamDb     已有的字段说明
     * @param paramDescDbDto 已有参数说明工具
     */
    private static void compareFields(ApiParam apiParamDescIn, List<ApiParamField> noRltFieldList, ApiParam apiParamDb,
                                      ParamDescDbDto paramDescDbDto) {
        // 1.是否不是无效数据
        if (Objects.isNull(apiParamDescIn) || CollectionUtils.isEmpty(apiParamDescIn.getFields())) {
            return;
        }

        // 2.需要使用数据
        Map<String, ApiParamField> fieldKeyAndFiledDbMap = paramDescDbDto.getFieldKeyAndFiledDbMap();
        List<ApiParamField> fieldInList = apiParamDescIn.getFields();
        if (CollectionUtils.isEmpty(apiParamDb.getFields())) {
            // 3.1.已有参数说明没有字段说明
            for (ApiParamField fieldIn : fieldInList) {
                String fieldInKey = ApiFieldUtil.getFieldKey(fieldIn);
                noRltFieldList.add(fieldIn);
                if (fieldKeyAndFiledDbMap.containsKey(fieldInKey)) {
                    // 接口已包含该字段说明则直接使用
                    ApiFieldUtil.copyValueNoKey(fieldKeyAndFiledDbMap.get(fieldInKey), fieldIn);
                }
                fieldIn.setParamId(apiParamDb.getId());
                // 没有必要设置
                if (CollectionUtils.isEmpty(apiParamDb.getFieldKeys())) {
                    apiParamDb.setFieldKeys(new HashSet<>(fieldInList.size()));
                }
                apiParamDb.getFieldKeys().add(fieldInKey);
            }
        } else {
            // 3.2.已有参数说明有参数说明
            if (CollectionUtils.isEmpty(apiParamDb.getFieldKeys())) {
                apiParamDb.setFieldKeys(new HashSet<>(fieldInList.size()));
            }

            for (ApiParamField fieldIn : fieldInList) {
                String fieldInKey = fieldIn.getFieldName();
                if (apiParamDb.getFieldKeys().contains(fieldInKey)) {
                    // 已包含该字段说明 设置id
                    fieldIn.setId(apiParamDb.getNameAndFieldMap().get(fieldInKey).getId());
                    continue;
                }
                if (fieldKeyAndFiledDbMap.containsKey(fieldInKey)) {
                    // 接口已包含、参数说明不包含该字段
                    apiParamDb.getFieldKeys().add(fieldInKey);
                    ApiFieldUtil.copyValueNoKey(fieldKeyAndFiledDbMap.get(fieldInKey), fieldIn);
                }
                fieldIn.setParamId(apiParamDb.getId());
                noRltFieldList.add(fieldIn);
            }
        }
    }

    /**
     * 接口对应参数说明还未存在
     *
     * @param apiParamDescIn 参数字符串转换的参数说明
     * @param noRltFieldList 没有绑定关系的字段说明
     * @param paramDescDbDto 已有参数说明工具
     */
    private static void paramAndNoRltFieldAdd(ApiParam apiParamDescIn, List<ApiParamField> noRltFieldList,
                                              ParamDescDbDto paramDescDbDto) {
        // 1.创建参数说明对象
        apiParamDescIn.setCreateTime(paramDescDbDto.getNow()).setUpdateTime(paramDescDbDto.getNow());

        Map<String, ApiParamField> fieldKeyAndFiledDbMap = paramDescDbDto.getFieldKeyAndFiledDbMap();
        if (!CollectionUtils.isEmpty(apiParamDescIn.getFields())) {
            // 2.设置字段说明对象
            for (ApiParamField fieldIn : apiParamDescIn.getFields()) {
                String fieldInKey = ApiFieldUtil.getFieldKey(fieldIn);
                noRltFieldList.add(fieldIn);
                if (fieldKeyAndFiledDbMap.containsKey(fieldInKey)) {
                    // 接口已包含该字段说明则直接使用
                    ApiFieldUtil.copyValueNoKey(fieldKeyAndFiledDbMap.get(fieldInKey), fieldIn);
                }
            }
        }
    }
}
